package utilities;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import model.Field;
import model.Player;

/**
 * Vind een pad in het speelveld vanaf de vijand naar de speler.
 */
public class PathFinder {

    private ArrayList<AbstractGraph.Edge> edges;
    private ArrayList<Field> vertices;
    private HashMap<Field, Boolean> processed;
    private int playerIndex;
    private int rootIndex;

    public PathFinder() {
        this.edges = new ArrayList<>();
        this.vertices = new ArrayList<>();
        this.processed = new HashMap<>();
        this.rootIndex = 0;
        this.playerIndex = 0;
    }

    public void scan(Field root) {
        // leegmaken van de vertices edges en processed list en de index van de enemy bepalen
        this.edges.clear();
        this.vertices.clear();
        this.processed.clear();

        // veld van de enemy op in de processed lijst zetten
        this.processed.put(root, false);
        boolean endCondition = false;

        while (!endCondition) {
            Field current = null;
            // bepalen van het eerste false veld of de eind conditie
            Iterator<Map.Entry<Field, Boolean>> it = this.processed.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<Field, Boolean> entry = it.next();
                // als er een false processed is dan hebben een een current
                if (!entry.getValue()) {
                    current = entry.getKey();
                    if (!this.vertices.contains(current)) {
                        this.vertices.add(current);
                    }
                    break;
                }
            }
            // kijkt of er al aan de
            if (current != null) {

                setVerticeEdge(current, Field.Directions.RIGHT);
                setVerticeEdge(current, Field.Directions.LEFT);
                setVerticeEdge(current, Field.Directions.UP);
                setVerticeEdge(current, Field.Directions.DOWN);

                processed.remove(current);
                processed.put(current, true);
            } else {
                endCondition = true;
            }
        }
    }

    private void setVerticeEdge(Field current, Field.Directions direction) {
        if (current.getNeighbor(direction) != null) {
            Field neighbor = current.getNeighbor(direction);
            // alleen als er geen Part zit in een veld of als er een speler inzit wordt hij meegenomen in de graaf.
            if (neighbor.getPart() == null || neighbor.getPart() instanceof Player) {
                if (!processed.containsKey(neighbor)) {
                    processed.put(neighbor, false);
                    if (!vertices.contains(neighbor)) {
                        vertices.add(neighbor);
                        if (neighbor.getPart() instanceof Player) {
                            this.playerIndex = vertices.indexOf(neighbor);
                        }
                    }
                }
                // voor het opbouwen van een adjencency matrix zijn biede richtingen van de edges nodig
                edges.add(new AbstractGraph.Edge(vertices.indexOf(current), vertices.indexOf(neighbor)));
                edges.add(new AbstractGraph.Edge(vertices.indexOf(neighbor), vertices.indexOf(current)));
            }
        }
    }
    
    public List<Field> getPath(){
        
         UnweightedGraph<Field> graph = new UnweightedGraph<>(this.edges, this.vertices);
         AbstractGraph.Tree bfs = graph.bfs(rootIndex);
         
           if (playerIndex < bfs.getNumberOfVerticesFound()) {
               List path = bfs.getPath(playerIndex);
               return path;
           }
           return null;
    }
    
    public boolean hasValidPath(){
        List path = this.getPath();
        if(path != null){
            if(path.contains(this.vertices.get(playerIndex))){
                return true;
            }
        }
        return false;
    }
    
    
    
    
}
